/*
 *    Copyright (c) 2018-2025, proj All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the proj developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: proj
 */

package com.ykht.proj.auth.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.ykht.proj.admin.api.dto.UserInfo;
import com.ykht.proj.admin.api.entity.SysUser;
import com.ykht.proj.admin.api.feign.RemoteUserService;
import com.ykht.proj.common.core.constant.CacheConstants;
import com.ykht.proj.common.core.constant.CommonConstants;
import com.ykht.proj.common.core.constant.SecurityConstants;
import com.ykht.proj.common.core.util.R;
import com.ykht.proj.common.security.component.ResourceAuthExceptionEntryPoint;
import com.ykht.proj.common.security.service.ProjUser;
import com.ykht.proj.common.security.service.ProjUserDetailsService;
import com.ykht.proj.coupon.Enum.UserTypeEnum;
import com.ykht.proj.coupon.api.feign.RemotePersonService;
import com.ykht.proj.coupon.api.feign.RemoteUserExtService;
import com.ykht.proj.coupon.dto.UserExtDto;
import com.ykht.proj.coupon.entity.CnPerson;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 用户详细信息
 *
 * @author proj
 */
@Slf4j
@Service
@AllArgsConstructor
public class ProjUserDetailsServiceImpl implements ProjUserDetailsService {

	private final RemoteUserService remoteUserService;
	private final RemotePersonService remotePersonService;
	private final CacheManager cacheManager;
	private final RemoteUserExtService remoteUserExtService;

	/**
	 * 用户密码登录
	 * @param username 用户名
	 * @return
	 * @throws UsernameNotFoundException
	 */
//	@Override
//	@SneakyThrows
//	public UserDetails loadUserByUsername(String username) {
//		Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
//		if (cache != null && cache.get(username) != null) {
//			return (ProjUser) cache.get(username).get();
//		}
//
//		R<UserInfo> result = remoteUserService.info(username, SecurityConstants.FROM_IN);
//		UserDetails userDetails = getUserDetails(result);
//		cache.put(username, userDetails);
//		return userDetails;
//	}

	/**
	 * 用户密码登录
	 * @param username 用户名
	 * @return
	 * @throws UsernameNotFoundException
	 */
	@Override
	public UserDetails loadUserByUsername(String username) {
		R<UserInfo> result = remoteUserService.info(username, SecurityConstants.FROM_IN);
		if (result.getCode()==CommonConstants.FAIL) throw new RuntimeException("用户登录失败");
		UserDetails userDetails = getUserDetails(result);
		Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
		cache.put(username, userDetails);
		return userDetails;
	}

	public List checkBind(Integer userType, Long loginId) {
		List list = new ArrayList();
		// 请求是否绑定了 公司品牌门店
		if (userType.equals(UserTypeEnum.company.getValue()) ||userType.equals(UserTypeEnum.brand.getValue())
				||userType.equals(UserTypeEnum.shop.getValue())||userType.equals(UserTypeEnum.customer_company.getValue())){
			UserExtDto userExt = new UserExtDto();
			userExt.setUserId(loginId.intValue());
			userExt.setUserType(userType);
			userExt.setStatus(0);//上架
			R<List> r = remoteUserExtService.getUserExtList(userExt,SecurityConstants.FROM_IN);
			if (r.getCode()==CommonConstants.FAIL) throw new RuntimeException("用户登陆失败");
			if (null==r.getData()||r.getData().size()==0) throw new RuntimeException("用户未绑定"+UserTypeEnum.getDescByVal(userType));
			list = r.getData();
			//品牌返回的参数特殊处理一下，brandName  改为name
			if (userType.equals(UserTypeEnum.brand.getValue())){
				String text = "brandName", newText = "name";
				List<Map<String,Object>>  brandList = list;
				brandList.stream().forEach(map->{
					map.put(newText,map.get(text));
					map.remove(text);
				});
				list = brandList;
			}
		}
		return list;
	}

/**
	 * 用户密码登录
	 * @param username 用户名
	 * @return
	 * @throws UsernameNotFoundException
	 *//*

	@Override
	public UserDetails loadUserByUsername(String username) {
		Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);

		ProjUser cacheUser = null;
		UserDetails userDetails = null;
		Integer userType = null;
		Integer loginId = null;
		if (cache != null && cache.get(username) != null) {
			cacheUser = (ProjUser) cache.get(username).get();
			userType = cacheUser.getUserType();
			loginId = cacheUser.getId();
		}else {
			R<UserInfo> result = remoteUserService.info(username, SecurityConstants.FROM_IN);
			if (result.getCode()==CommonConstants.FAIL) throw new RuntimeException("用户登陆失败");
			userDetails = getUserDetails(result);
			UserInfo userInfo = result.getData();
			userType = userInfo.getSysUser().getUserType();
			loginId = userInfo.getSysUser().getUserId();
		}
		// 请求是否绑定了 公司品牌门店
		this.checkBind(userType,loginId.longValue());

		if (cache != null && cache.get(username) != null) {
			return cacheUser;
		}else {
			cache.put(username, userDetails);
			return userDetails;
		}
	}
*/

	/**
	 * // 请求是否绑定了 公司品牌门店
	 * @param userType
	 * @param loginId
	 */



	/**
	 * 根据社交登录code 登录
	 * @param inStr TYPE@CODE
	 * @return UserDetails
	 * @throws UsernameNotFoundException
	 */
	@Override
	@SneakyThrows
	public UserDetails loadUserBySocial(String inStr) {
		return getUserDetails(remoteUserService.social(inStr, SecurityConstants.FROM_IN));
	}

	@Override
	public Object loadUserByApplet(String code) throws UsernameNotFoundException {
		R r = remotePersonService.loadUserByApplet(code, SecurityConstants.FROM_IN);

		CnPerson person = null;
		if(r.getData()!=null){
			person = new CnPerson();
			BeanUtil.copyProperties(r.getData(),person);
		}
		return person;
	}
	/**
	 * 构建userdetails
	 * @param result 用户信息
	 * @return
	 */
	private UserDetails getUserDetails(R<UserInfo> result) {
		if (result == null || result.getData() == null) {
			throw new UsernameNotFoundException("用户不存在");
		}

		UserInfo info = result.getData();
		Set<String> dbAuthsSet = new HashSet<>();
		if (ArrayUtil.isNotEmpty(info.getRoles())) {
			// 获取角色
			Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId));
			// 获取资源
			dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));

		}
		Collection<? extends GrantedAuthority> authorities = AuthorityUtils
				.createAuthorityList(dbAuthsSet.toArray(new String[0]));
		SysUser user = info.getSysUser();
		// 请求是否绑定了 公司品牌门店
//		this.checkBind(user.getUserType(),user.getUserId().longValue());

		boolean enabled = StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL);

		List sjList = this.checkBind(user.getUserType(),user.getUserId().longValue());
		Long sjId = user.getSjIdDefLogin();
		if (sjId==null&&sjList.size()>0) sjId = MapUtil.getLong((Map)sjList.get(0),"id");

		// 构造security用户
		return new ProjUser(sjList,sjId,user.getUserId(), user.getDeptId(), user.getUserType(),user.getNickName(),user.getPhone(), user.getAvatar(), user.getTenantId(),
				user.getUsername(), SecurityConstants.BCRYPT + user.getPassword(), enabled, true, true,
				!CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities);
	}

}
